﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Bling.DSL;
using Bling.WPF;
using Bling.Arrays;
using Bling.Shapes;
using Bling.Animation;
using Bling.Util;
using Bling.Physics;
using Bling.D3D;
using Bling.Lighting;


namespace Bling.PhysicsTutorial {
  public class NewCurtainBlock : ImmediatePhysicsBlock<NewCurtainBlock>, Position3DBody.Is<NewCurtainBlock>, MeshBody.Is<NewCurtainBlock> {
    public class Config {
      public DoubleBl UseX = 1d;
      public DoubleBl SpringX = 0d;
      public DoubleBl OpenHole = 0d;
      public DoubleBl ConsumeWidth = 1d;
      public Func<DoubleBl> OpenZ;
      public BoolBl Do3D = false;
      public bool DoRight = false;
      public BoolBl DoTied = false;
      public bool AddTextBox = false;
      public BrushBl Brush = Brushes.DarkBlue;
    }
    public readonly CurtainUI CurtainUI;
    public NewCurtainBlock(World World, int ColumnCount, int RowCount, CanvasBl MainCanvas, Config Config)
      : base(World, ColumnCount * RowCount) {

      new Position3DBody.Impl<NewCurtainBlock>(this);
      new MeshBody.Impl<NewCurtainBlock>(this, ColumnCount, RowCount);

      this.CurtainUI = new CurtainUI() {
        Open = Config.UseX, 
        Consume = new PointBl(Config.ConsumeWidth, 1d), PinTop = true,
        Background = Config.Brush,
        DoRight = Config.DoRight,
        AddTextBox = Config.AddTextBox,

      };
      if (Config.OpenZ != null) CurtainUI.OpenZ = Config.OpenZ;

      CurtainUI.Init(this, MainCanvas);
      CurtainUI.Viewport.Visibility = Config.Do3D;
      /**/
      this.MakeThumbGrid(MainCanvas, CurtainUI.MeshSize, CurtainUI.Viewport.Bounds, (i, j, thumb) => {
        thumb.ZIndex = 99;
        thumb.CanDrag = true;
      });
       
      this.MakeLineGrid(MainCanvas, CurtainUI.MeshSize, CurtainUI.Viewport.Bounds, (i, j, shape) => {
        DoubleBl di = ((DoubleBl)i);
        di = di / ((double)(this.ColumnCount() - 2));
        DoubleBl dj = j;
        dj = dj / ((double)(this.RowCount() - 2));
        shape.Fill = (di.Lerp(Colors.Red, Colors.Blue) * .5 +
                    dj.Lerp(Colors.Green, Colors.Purple) * .5);
        shape.Visibility = !Config.Do3D;
      });

      Func<Body, DoubleBl> MassF = (body) => {
        return 1d;
      };

      ForAll(body => {
        DoubleBl DoOpen = (Config.OpenHole & body.Row() == (this.RowCount() / 2));
        var totied = Config.DoTied & (body.Row() <= 3) & (Config.OpenHole == 0);
        var p = body.Position3D();
        p.VerletStep(.05);
        // first row's x,y are fixed.
        {
          var ratio = Config.UseX * ((4 - 1.0d.Bl()*body.Row()) / 4d).Sqrt();
          var t1 = (totied & !Config.DoRight.Bl()).Condition(ratio, 1.0);
          var t2 = (totied & Config.DoRight.Bl()).Condition(this.MeshBodyE().CellSize.X * (1.0 - ratio) * (this.ColumnCount() - body.Column()), 0);
          var x = this.MeshBodyE().LeftTop.X + body.Column() * this.MeshBodyE().CellSize.X * t1 + t2;

          var y = this.MeshBodyE().LeftTop.Y + body.Row() * this.MeshBodyE().CellSize.Y;
          var w = p;
          BoolBl IsLockedColumnn = Config.DoRight.Bl().Condition(body.IsLastColumn(), body.IsFirstColumn());

          w = (body.IsFirstRow() | IsLockedColumnn | totied).Condition(new PointBl(x, y), w);
          w = 0.3.Bl().Lerp(p, w);
          var dy = y - p.Y;
          var gravity = (!IsLockedColumnn).Condition((dy > 0).Condition(dy, 0d), 0);
          w += new Point3DBl(0, gravity, 0);

          w = (Config.OpenHole & Config.DoRight.Bl().Condition(body.Column() < 2, body.Column() > this.ColumnCount() - 2) &
               body.Row() >= this.RowCount() / 2 - 1 & body.Row() <= this.RowCount() / 2 + 1).Lerp(w, new Point3DBl(w.XY, .5d));
          p.Bind = w;
        }
        Point3DBl q = new Point3DBl(0, 0, 0);
        DoubleBl N = 0d;
        {
          var pLo = body.CursorPosition3D().Spring(0, -1);
          var pHi = body.CursorPosition3D().Spring(0, +1);
          var mLo = body.IsFirstRow().Condition(0d, MassF(this.Get(body.Column(), body.Row() - 1)));
          var mHi = body.IsLastRow().Condition(0d, MassF(this.Get(body.Column(), body.Row() + 1)));
          var mAt = MassF(body);
          q += ((pLo * mLo + p * mAt) / (mLo + mAt)) * mLo;
          q += ((pHi * mHi + p * mAt) / (mHi + mAt)) * mHi;
          N += mLo + mHi;
        }
        {
          var widthX = this.MeshBodyE().CellSize.X;
          widthX = DoOpen.Lerp(widthX, widthX / 4d);
          widthX = (Config.OpenHole & body.Row() >= this.RowCount() / 2 - 1 & 
                                      body.Row() <= this.RowCount() / 2 + 1).Lerp(widthX, widthX / 2d);

          var pLo = p.Spring(this.Get(body.Column() - 1, body.Row()).Position3D(), widthX);
          var pHi = p.Spring(this.Get(body.Column() + 1, body.Row()).Position3D(), widthX);

          var mLo = body.IsFirstColumn().Condition(0d, MassF(this.Get(body.Column() - 1, body.Row()))) * Config.SpringX;
          var mHi = body.IsLastColumn().Condition(0d, MassF(this.Get(body.Column() + 1, body.Row()))) * Config.SpringX;
          var mAt = MassF(body);
          q += ((pLo * mLo + p * mAt) / (mLo + mAt)) * mLo;
          q += ((pHi * mHi + p * mAt) / (mHi + mAt)) * mHi;
          N += mLo + mHi;
        }
        //for (int i = 0; i < 2; i++)
        q = q / N;
        p.Bind = q;
        // fix column min/max
        if (true) {
          var x = p.X;
          var y = p.Y;
          // we can't be before/ahead our previous column
          var lo = this.Get(body.Column() - 1, body.Row()).Position3D();
          var hi = this.Get(body.Column() + 1, body.Row()).Position3D();

          x = (x < lo.X & !body.IsFirstColumn()).Condition(lo.X, x);
          x = (x > hi.X & !body.IsLastColumn()).Condition(hi.X, x);

          //??
          y = (!body.IsFirstColumn() & !body.IsLastColumn() & y < hi.Y & y > lo.Y).Condition(y,//(lo.Y + hi.Y)/2.0 ?
            (!body.IsFirstColumn()).Condition(lo.Y,
            (!body.IsLastColumn()).Condition(hi.Y, 0d)));
          p.Relax[.5] = new PointBl(x, y);
        }
      });
      CustomActivate = (block) => {
        CurtainUI.Activate(block);
      };
    }
  }

  public class NewCurtain : PhysicsCanvas {
    public NewCurtain() : base("Curtain2", 30) { }
    protected override Action Init(CanvasBl MainCanvas, World World) {
      var d = new GeometryDrawingBl() {
        Brush = Brushes.Maroon,
        Pen = new PenBl() { Thickness = 0.5, Brush = Brushes.Black, },
        Geometry = new PathGeometryBl() {
          Figures = {
            AddOne = new PathFigureBl() {
              StartPoint = new PointBl(0, 0),
              IsFilled = true,
              Segments = {
                AddMany = new PathSegmentBl[] {
                new LineSegmentBl() { Point = new PointBl(50,0) },
                new LineSegmentBl() { Point = new PointBl(50,50) },
                new LineSegmentBl() { Point = new PointBl(0,50) },
                new LineSegmentBl() { Point = new PointBl(0,0) },
              },
              },
            },
          },
        },
      };
      var brush = new DrawingBrushBl() {
        Drawing = d, Stretch = Stretch.None, TileMode = TileMode.FlipXY,
        View = {
          Port = {
            Units = BrushMappingMode.Absolute,
            Dim = new Point4DBl(new PointBl(0, 0), new PointBl(50, 50)),
          },
        },
      };

      MainCanvas.Background = Brushes.Black;
      var UseX = new LabelSliderBl(MainCanvas) {
        LeftTop = new PointBl(0, 0), ZIndex = 100,
        Minimum = 0.001d, Maximum = 1d, Value = 1d, LabelName = "UseX",
      };
      var OpenHole = new CheckBoxBl(MainCanvas) {
        LeftTop = UseX.LeftBottom, ZIndex = 100,
        Content = "Open",
      };
      var Do3D = new CheckBoxBl(MainCanvas) {
        LeftTop = OpenHole.RightTop, ZIndex = 100,
        Content = "3D", IsChecked = true, 
      };
      var DoTied = new CheckBoxBl(MainCanvas) {
        LeftTop = Do3D.RightTop, ZIndex = 100,
        Content = "Tied", IsChecked = false, 
      };
      var spX = new LabelSliderBl(MainCanvas) {
        LeftTop = OpenHole.LeftBottom, ZIndex = 100,
        Minimum = 0.001d, Maximum = 1d, Value = 1d, LabelName = "SpringX",
      };

      var leftCurtain = new NewCurtainBlock(World, 15, 7, MainCanvas, new NewCurtainBlock.Config() {
        ConsumeWidth = 0.5,
        UseX = UseX, SpringX = 1d, OpenHole = OpenHole, Do3D = Do3D, DoRight = false,
        Brush = brush, OpenZ = () => 0.35 * (1d - UseX.Value).Max(OpenHole.IsChecked.Condition(1d, 0d)),
      });
      var lc = leftCurtain.CurtainUI.Cloth;
      var lbl = new LabelBl(lc) {
        Background = Brushes.Transparent, Foreground = Brushes.Black,
        Font = { Weight = FontWeights.SemiBold, Size = 40d },
        Width = lc.Width, LeftBottom = new PointBl(0, lc.Height),
      };
      var lbl0 = new TextBoxBl(MainCanvas) {
        ZIndex = leftCurtain.CurtainUI.Viewport.ZIndex + 1,
        //Border = { Thickness = 1, Brush = Brushes.Black },
        Font = lbl.Font,
        Background = Brushes.Transparent,
        Foreground = Brushes.Transparent,
        Text = "Hello",
        Width = lbl.Width, 
        LeftBottom = new PointBl(0, MainCanvas.Height), // + leftCurtain.CurtainUI.Viewport.LeftTop,
      };
      lbl.Content = lbl0.Text;


      new NewCurtainBlock(World, 15, 7, MainCanvas, new NewCurtainBlock.Config() {
        ConsumeWidth = 0.5,
        UseX = UseX, SpringX = spX, OpenHole = OpenHole, Do3D = Do3D, DoTied = DoTied, DoRight = true,
        Brush = brush, OpenZ = () => 0.35, AddTextBox = false,
      });
      return () => {
        Action<int> doSomething = Point3DBl.Assign<int>(i => leftCurtain[i].Position3D(), i => leftCurtain[i].Position3D() + new Point3DBl(.0, .0, -0.2));


        var translateX = IntBl.Func<double>((x0) => {
          var x = x0.Bl();
          x = x / leftCurtain.CurtainUI.Viewport.Width * leftCurtain.CurtainUI.Open;
          //x = x * leftCurtain.CurtainUI.MeshSize.X;
          return (IntBl) (x * leftCurtain.ColumnCount()).Round;
        });

       

        var cbox = lbl0.CurrentValue;
        cbox.TextChanged += (x, y) => {
          if (y.Changes.Count == 1) {
            foreach (var change in y.Changes) {
              if ((change.AddedLength == 1 && change.RemovedLength == 0)
              || (change.AddedLength == 0 && change.RemovedLength == 1)) {
                var xPos = cbox.GetRectFromCharacterIndex(cbox.Text.Length).Left;
                var column = translateX(xPos);
                if (column < leftCurtain.ColumnCount())
                  for (int row = 0; row < leftCurtain.RowCount(); row++) 
                    doSomething(row * leftCurtain.ColumnCount() + column);
              }
            }
          }
        };
      };

    }
  }


}
